// Author	: damphat@gmail.com
// Created	: 01/12/2008

#pragma once
#include "vn/NetCommon.h"
#include "vn/IPEndPoint.h"
#include "vn/IPAddress.h"
#include "vn/IPHostEntry.h"
#include "vn/Socket.h"
#include "vn/NetworkStream.h"


#define _GETSET_FROM_SOCKET(socket, PropType, PropName)\
	PropType Get##PropName() {\
		return socket.Get##PropName();\
	}\
	void Set##PropName(PropType val) {\
		socket.Set##PropName(val);\
	}

#define _GET_FROM_SOCKET(socket, PropType, PropName)\
	PropType Get##PropName() {\
		return socket.Get##PropName();\
	}



NET_BEGIN

class TcpClient {
public:
	TcpClient() { }

	void Connect(const IPEndPoint &ep) {
		CHECK_STATUS(m_Client.m_Status == StatusClose); //TcpClient must be closed before this operation.
		m_Client.OpenTcp();
		try {
			m_Client.Connect(ep);
		}
		catch(SocketException& e) {
			m_Client.Close();	// return back to original state
			throw e;
		}
	}

	void Connect(const IPAddress &address, int port) {
		CHECK_ARG_RANGE_PORT(port);
		CHECK_STATUS(m_Client.m_Status == StatusClose);
		Connect(IPEndPoint(address, port));
	}

	void Connect(String hostname, int port) {
		CHECK_ARG_NOT_NULL(hostname.C_STR());
		CHECK_ARG_RANGE_PORT(port);
		CHECK_STATUS(m_Client.m_Status == StatusClose);
		IPHostEntry ent = Dns::GetHostEntry(hostname);
		Connect(ent.AddressList[0], port);
	}

	void Close() {
		m_Client.Close();
	}

	// usecase?: Stream x = TcpClient::GetStream();
	void GetStream(NetworkStream &out) {
		out.Attach(&m_Client, false);
	}

	NetworkStream *GetStream() {
		NetworkStream *pns = new NetworkStream();

		try {
			pns->Attach(&m_Client, false);
		}
		catch(...) {
			delete pns;
			throw;
		}

		return pns;
	}


	virtual ~TcpClient() {
	}

public:
	Socket m_Client;
	_GET_FROM_SOCKET   (m_Client, SocketStatus,	Status);

	_GET_FROM_SOCKET   (m_Client, int,		Available);
	//bool Connected { get; }
	_GETSET_FROM_SOCKET(m_Client, bool,		ExclusiveAddressUse);
	_GETSET_FROM_SOCKET(m_Client, LINGER,	LingerState);
	_GETSET_FROM_SOCKET(m_Client, bool,		NoDelay);
	_GETSET_FROM_SOCKET(m_Client, int,		ReceiveBufferSize);
	_GETSET_FROM_SOCKET(m_Client, int,		ReceiveTimeout);
	_GETSET_FROM_SOCKET(m_Client, int,		SendBufferSize);
	_GETSET_FROM_SOCKET(m_Client, int,		SendTimeout);
};

NET_END
